home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 July / macformat-026.iso / mac / Shareware City / Developers / PopupCDEF-10b5 / Source / PopupCDEF.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-26  |  9.6 KB  |  330 lines  |  [TEXT/KAHL]

  1. /* See the file Distribution for distribution terms.
  2.     (c) Copyright 1994 Ari Halberstadt */
  3.  
  4. /*    This file contains a popup CDEF that uses the file PopupLib.c to handle
  5.     the details of popup menus. This file contains glue to link the Control
  6.     Manager and the routines in PopupLib.c.
  7.  
  8.     94/11/26 aih
  9.     - Automatically adjusts control's min, max
  10.     
  11.     94/07/06 aih
  12.     - Adapted for universal headers
  13.  
  14.     94/03/15 aih
  15.     - Added check against kPopupVersion before a CDEF is attached during
  16.     debugging with PopupCDEFAttach.
  17.     - Moved description to documentation file.
  18.     - Uses bullet instead of check mark as mark character when using
  19.     window font.
  20.     - Added support for the popupFixedWidth variation code.
  21.     
  22.     94/01/19 aih
  23.     - Added call to GetMenuHandle to allow use of menus created by the application
  24.     that are not in the resource file. Also fixed use of an already disposed
  25.     handle in the CDEF's dispose routine. Thanks to Eric Bowman (bobo@reed.edu)
  26.     for both of these.
  27.      
  28.     93/12/31 aih
  29.     - Returns 1 as part code (same as 7.0 CDEF) instead of inCheckBox
  30.     
  31.     93/12/26 aih
  32.     - Major overhaul. Now supports most of the features of the System 7.0 CDEF.
  33.     - Added functions for installing a glue handle to the popup CDEF,
  34.     allowing the CDEF to be debugged from within an application. The only
  35.     message that can't be debugged this way is the initCntl message.
  36.     
  37.     93/12/24 aih
  38.     - Adapted to use PopupHandle type
  39.     - Removed dependence on other libraries
  40.     - Simplified by removing complicated variation setting code
  41.     based on parsing the title string
  42.     
  43.     91/03/15 aih
  44.     - Fixed bug which caused a crash if the menu resource specified in the
  45.     control's refCon field couldn't be loaded
  46.     
  47.     91/03/04-05 Ari Halberstadt (aih)
  48.     - Created this file */
  49.     
  50. #include <limits.h>
  51. #include <LowMem.h>
  52. #include <Memory.h>
  53. #include <Resources.h>
  54. #include <SetUpA4.h>
  55. #include <TextEdit.h>
  56. #include <ToolUtils.h>
  57. #include "PopupLib.h"
  58.  
  59. /* mask for the low 31 bits of the calcCRgns message to CDEFs */
  60. #define calcCRgnsMask        (0x7fffffffL)
  61.  
  62. /* hilite value for a disabled control */
  63. #define kControlDisabled    (255)
  64.  
  65. /* System software version needed to use this CDEF. Tested with systems
  66.     6.0.5, 7.0, and 7.1.1, but may work on systems as early as 4.0.1. */
  67. #define kSystemVersion        (0x0605)
  68.  
  69. /* The checkMark character is only present in the Chicago system font,
  70.     whereas the bullet character ('•') is present in most fonts. When the
  71.     popupUseWFont variation code is used, we change the mark character
  72.     from the default checkMark to kBulletMark. */
  73. #define kBulletMark            ('•')
  74.  
  75. /*    When debugging, it's convenient to have the CDEF ignore the
  76.     initCntl message, so that we can send it our own message
  77.     to initialize it when we call PopupCDEFAttach. */
  78. #ifndef NDEBUG
  79.     #define initCntlMsg        (1234)
  80. #else /* NDEBUG */
  81.     #define initCntlMsg        initCntl
  82. #endif /* NDEBUG */
  83.  
  84. /* draw the control */
  85. static void Draw(ControlHandle ctl, PopupHandle popup)
  86. {
  87.     Str255 title;
  88.     Rect bounds;
  89.  
  90.     GetControlTitle(ctl, title);
  91.     bounds = (**ctl).contrlRect;
  92.     PopupDrawSet(popup, false);
  93.     PopupTitleSet(popup, title);
  94.     PopupBoundsSet(popup, &bounds);
  95.     PopupCurrentSet(popup, (**ctl).contrlValue);
  96.     PopupVisibleSet(popup, (**ctl).contrlVis != 0);
  97.     PopupEnableSet(popup, (**ctl).contrlHilite != kControlDisabled);
  98.     PopupDrawSet(popup, true);
  99.     PopupCalculate(popup);
  100.     PopupDraw(popup);
  101. }
  102.  
  103. /* return the part of the control that the point is in */
  104. static long Test(ControlHandle ctl, PopupHandle popup, Point where)
  105. {
  106.     return(PopupWithin(popup, where) ? kPopupPartCode : 0);
  107. }
  108.  
  109. /* calculate the region containing the control */
  110. static void Calculate(ControlHandle ctl, PopupHandle popup, RgnHandle rgn)
  111. {
  112.     Rect bounds;
  113.     
  114.     PopupBounds(popup, &bounds);
  115.     RectRgn(rgn, &bounds);
  116. }
  117.  
  118. /* load the menu and set 'gotmenu' to true if we called GetMenu */
  119. static MenuHandle LoadMenu(short id, Boolean *gotmenu)
  120. {
  121.     MenuHandle    menu;        /* the menu */
  122.     ProcPtr        errproc;    /* for saving and restoring ResErrProc */
  123.     short            load;        /* for saving and restoring ResLoad */
  124.     
  125.     /* we can tell if the menu hasn't been loaded by a call to GetMenu
  126.         since the menu handle will be a nil resource handle */
  127.     *gotmenu = false;
  128.     load = LMGetResLoad();
  129.     LMSetResLoad(false);
  130.     menu = (MenuHandle) GetResource('MENU', id);
  131.     LMSetResLoad(load);
  132.     if (menu && ! *menu) {
  133.         /* The menu is in the resource file, but it hasn't been loaded
  134.             yet, so we have to call GetMenu. */
  135.         errproc = LMGetResErrProc();
  136.         LMSetResErrProc(NULL);
  137.         menu = GetMenu(id);
  138.         LMSetResErrProc(errproc);
  139.         *gotmenu = true;
  140.     }
  141.     else if (! menu) {
  142.         /* To allow use of menus created by the application that are not in the
  143.             resource file we look for the menu in the menu list. */
  144.         menu = GetMenuHandle(id);
  145.     }
  146.     return(menu);
  147. }
  148.  
  149. /* initialize the control */
  150. static void Initialize(ControlHandle ctl, short var)
  151. {
  152.     MenuHandle    menu;        /* the menu */
  153.     PopupHandle popup;    /* handle to the popup menu */
  154.     Rect            bounds;    /* control's bounding rectangle */
  155.     short            nitems;    /* number of items in menu */
  156.     Boolean        gotmenu;    /* true if we created the menu by calling GetMenu */
  157.     
  158.     menu = LoadMenu((**ctl).contrlMin, &gotmenu);
  159.     if (menu) {
  160.         if (gotmenu && (var & popupUseAddResMenu) != 0)
  161.             AppendResMenu(menu, (**ctl).contrlRfCon);
  162.         bounds = (**ctl).contrlRect;
  163.         popup = PopupBegin((**ctl).contrlOwner, menu, &bounds, ctl);
  164.         if (popup) {
  165.             (**popup).state.gotmenu = gotmenu;
  166.             PopupDrawSet(popup, false);
  167.             PopupTitleWidthSet(popup, (**ctl).contrlMax);
  168.             if (((**ctl).contrlValue & popupTitleNoStyle) == 0)
  169.                 PopupTitleStyleSet(popup, (**ctl).contrlValue >> CHAR_BIT);
  170.             if (((**ctl).contrlValue & popupTitleRightJust) == popupTitleRightJust)
  171.                 PopupJustSet(popup, teFlushRight);
  172.             if ((var & popupUseWFont) != 0)
  173.                 PopupMarkSet(popup, kBulletMark);
  174.             PopupTypeInSet(popup, (var & popupTypeIn) != 0);
  175.             PopupUseWFontSet(popup, (var & popupUseWFont) != 0);
  176.             PopupFixedWidthSet(popup, (var & popupFixedWidth) != 0);
  177.             PopupDrawSet(popup, true);
  178.             PopupCalculate(popup);
  179.             nitems = CountMItems(menu);
  180.             (**ctl).contrlMax = nitems;
  181.             (**ctl).contrlMin = (nitems > 0 ? 1 : 0);
  182.             (**ctl).contrlValue = (nitems > 0 ? 1 : 0);
  183.             (**ctl).contrlData = (Handle) popup;
  184.             (**ctl).contrlAction = (ControlActionUPP) -1L;
  185.         }
  186.     }
  187. }
  188.  
  189. /* dispose of the control */
  190. static void Dispose(ControlHandle ctl, PopupHandle popup)
  191. {
  192.     MenuHandle menu;
  193.     Boolean gotmenu;
  194.     
  195.     menu = (**popup).menu;
  196.     gotmenu = (**popup).state.gotmenu;
  197.     PopupEnd(popup);
  198.     if (gotmenu)
  199.         ReleaseResource((Handle) menu);
  200.     (**ctl).contrlData = NULL;
  201. }
  202.  
  203. /* track a mouse click in the control */
  204. static void Track(ControlHandle ctl, PopupHandle popup)
  205. {
  206.     short value;
  207.     
  208.     PopupSelect(popup);
  209.     value = PopupCurrent(popup);
  210.     (**ctl).contrlValue = value;
  211. }
  212.  
  213. /* entry point for CDEF */
  214. pascal long PopupCDEF(short var, ControlHandle ctl, short msg, long param)
  215. {
  216.     PopupHandle    popup = NULL;
  217.     SysEnvRec world;
  218.     long result = 0;
  219.     short nitems;
  220.     
  221.     /* setup global variables */
  222.     RememberA0();
  223.     SetUpA4();
  224.     
  225.     /* check system software */
  226.     (void) SysEnvirons(curSysEnvVers, &world);
  227.     if (world.systemVersion >= kSystemVersion) {
  228.         
  229.         /* execute message */
  230.         popup = (PopupHandle) (**ctl).contrlData;
  231.         if (msg == initCntlMsg) {
  232.             Initialize(ctl, var);
  233.             popup = (PopupHandle) (**ctl).contrlData;
  234.         }
  235.         else if (popup) {
  236.             nitems = CountMItems((**popup).menu);
  237.             (**ctl).contrlMax = nitems;
  238.             (**ctl).contrlMin = (nitems > 0 ? 1 : 0);
  239.             if ((**ctl).contrlValue > (**ctl).contrlMax)
  240.                 (**ctl).contrlValue = (**ctl).contrlMax;
  241.             if ((**ctl).contrlValue < (**ctl).contrlMin)
  242.                 (**ctl).contrlValue = (**ctl).contrlMin;
  243.             switch (msg) {
  244.             case drawCntl:
  245.                 param = LoWord(param); /* see TN196 */
  246.                 Draw(ctl, popup);
  247.                 break;
  248.             case testCntl:
  249.                 result = Test(ctl, popup, *(Point *) ¶m);
  250.                 break;
  251.             case calcCRgns:
  252.                 param &= calcCRgnsMask;
  253.                 /* no break */
  254.             case calcCntlRgn:
  255.             case calcThumbRgn:
  256.                 Calculate(ctl, popup, (RgnHandle) param);
  257.                 break;
  258.             case dispCntl:
  259.                 Dispose(ctl, popup);
  260.                 popup = NULL;
  261.                 break;
  262.             case autoTrack:
  263.                 param = LoWord(param); /* see TN196 */
  264.                 Track(ctl, popup);
  265.                 break;
  266.             }
  267.         }
  268.     }
  269.     RestoreA4();
  270.     return(result);
  271. }
  272.  
  273. #if CDEF
  274.  
  275. pascal long main(short var, ControlHandle ctl, short msg, long param)
  276. {
  277.     return(PopupCDEF(var, ctl, msg, param));
  278. }
  279.  
  280. #endif /* CDEF */
  281.  
  282. #if ! CDEF && ! NDEBUG
  283.  
  284. /*    Functions for attaching a glue handle so the CDEF can be debugged
  285.     from within an application. */
  286.  
  287. /* the structure installed in the contrlDefProc field */
  288. typedef struct {
  289.     short jmp;                    /* jump instruction */
  290.     void *addr;                    /* address of CDEF function */
  291.     Handle contrlDefProc;    /* saved value of contrlDefProc field */
  292. } PopupGlueType, *PopupGluePtr, **PopupGlueHandle;
  293.  
  294. /* Set the control's defproc field to a small glue handle that will
  295.     jump to PopupCDEF. This makes debugging a lot easier, since you
  296.     can then step through the code with a debugger. */
  297. void PopupCDEFAttach(ControlHandle ctl)
  298. {
  299.     PopupGlueHandle glue;
  300.     
  301.     if (! (**ctl).contrlData ||
  302.          PopupVersion((PopupHandle) (**ctl).contrlData) == kPopupVersion)
  303.     {
  304.         glue = (PopupGlueHandle) NewHandleClear(sizeof(PopupGlueType));
  305.         if (glue) {
  306.             (**glue).jmp = ASM_M68K_JMP;
  307.             (**glue).addr = PopupCDEF;
  308.             (**glue).contrlDefProc = (**ctl).contrlDefProc;
  309.             (**ctl).contrlDefProc = (Handle) glue;
  310.             if (! (**ctl).contrlData)
  311.                 (void) PopupCDEF(GetControlVariant(ctl), ctl, initCntlMsg, 0);
  312.         }
  313.     }
  314. }
  315.  
  316. /* Dispose of the glue handle created with PopupCDEFAttach and set the
  317.     control's contrlDefProc field to point to its original value. */
  318. void PopupCDEFDetach(ControlHandle ctl)
  319. {
  320.     PopupGlueHandle glue;
  321.     
  322.     if (PopupVersion((PopupHandle) (**ctl).contrlData) == kPopupVersion) {
  323.         glue = (PopupGlueHandle) (**ctl).contrlDefProc;
  324.         (**ctl).contrlDefProc = (**glue).contrlDefProc;
  325.         DisposeHandle((Handle) glue);
  326.     }
  327. }
  328.  
  329. #endif /* ! CDEF && ! NDEBUG */
  330.